package com.tencent.wxpay.common;

import com.tencent.wxpay.protocol.model.Coupon;
import com.tencent.wxpay.protocol.model.CouponRefund;
import com.tencent.wxpay.protocol.model.Refund;
import com.tencent.wxpay.protocol.model.RefundQuery;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * xml解析器
 */
public class XMLParser {

    private static final Logger logger = LoggerFactory.getLogger(XMLParser.class);

    /**
     * 从OrderQueryResponseString里面解析出代金券数据
     * @param orderQueryResponseString RefundQuery API返回的数据
     * @return 因为代金券数据有可能是多个，所以返回一个列表
     */
    public static List<Coupon> getOrderCouponList(String orderQueryResponseString) throws IOException, SAXException, ParserConfigurationException {
        List<Coupon> couponList = new ArrayList<>();

        Map<String,Object> map = XMLParser.getMapFromXML(orderQueryResponseString);

        int count = 0;
        if(null != map.get("coupon_count")){
            try {
                count = Integer.parseInt((String) map.get("coupon_count"));
            } catch (NumberFormatException e){}
        }
        logger.info("coupon_count:" + count);

        if(count<1) {
            return couponList;
        }

        for(int i=0;i<count;i++){
            Coupon coupon = new Coupon();
            coupon.setCoupon_id(Util.getStringFromMap(map,"coupon_id_" + i,""));
            coupon.setCoupon_fee(Util.getIntFromMap(map,"coupon_fee_" + i));
            coupon.setCoupon_type(Util.getStringFromMap(map,"coupon_type_" + i,""));
            couponList.add(coupon);
        }
        return couponList;
    }

    /**
     * 从RefundQueryResponseString里面解析出退款订单数据
     * @param refundResponseString RefundQuery API返回的数据
     * @return 因为订单数据有可能是多个，所以返回一个列表
     */
    public static List<Refund> getRefundCouponList(String refundResponseString) throws IOException, SAXException, ParserConfigurationException {
        List<Refund> refundList = new ArrayList<>();

        Map<String,Object> map = XMLParser.getMapFromXML(refundResponseString);
        // TODO 这个地方API文档写的有问题，我猜这个是数量，没有$n
        int count = 0;
        if(null != map.get("coupon_refund_count")){
            try {
                count = Integer.parseInt((String) map.get("coupon_refund_count"));
            } catch (NumberFormatException e){}
        }
        logger.info("coupon_refund_count:" + count);

        if(count < 1) {
            return refundList;
        }

        for(int i=0;i<count;i++){
            Refund refund = new Refund();
            refund.setCoupon_type(Util.getStringFromMap(map,"coupon_type_" + i, ""));
            refund.setCoupon_refund_fee(Util.getIntFromMap(map,"coupon_refund_fee_" + i));
            refund.setCoupon_refund_count(Util.getIntFromMap(map,"coupon_refund_fee_" + i));
            List<CouponRefund> couponRefundList = new ArrayList<>();
            if(refund.getCoupon_refund_count() > 0) {
                for(int j=0; j < refund.getCoupon_refund_count(); j++) {
                    CouponRefund couponRefund = new CouponRefund();
                    couponRefund.setCoupon_refund_batch_id(Util.getStringFromMap(map,"coupon_refund_batch_id_" + i + "_"+j,""));
                    couponRefund.setCoupon_refund_id(Util.getStringFromMap(map,"coupon_refund_id_" + i + "_"+j,""));
                    couponRefund.setCoupon_refund_fee(Util.getIntFromMap(map,"coupon_refund_fee_" + i + "_"+j));
                    couponRefundList.add(couponRefund);
                }
            }
            refund.setCouponRefunds(couponRefundList);
            refundList.add(refund);
        }
        return refundList;
    }

    /**
     * 从RefundQueryResponseString里面解析出退款订单数据
     * @param refundQueryResponseString RefundQuery API返回的数据
     * @return 因为订单数据有可能是多个，所以返回一个列表
     */
    public static List<RefundQuery> getRefundQueryList(String refundQueryResponseString) throws IOException, SAXException, ParserConfigurationException {
        List<RefundQuery> refundQueryList = new ArrayList<>();

        Map<String,Object> map = XMLParser.getMapFromXML(refundQueryResponseString);

        int count = 0;
        if(null != map.get("refund_count")){
            try {
                count = Integer.parseInt((String) map.get("refund_count"));
            } catch (NumberFormatException e){}
        }
        logger.info("refund_count:" + count);

        if(count<1) {
            return refundQueryList;
        }

        for(int i=0;i<count;i++){
            RefundQuery refundQuery = new RefundQuery();
            refundQuery.setOut_refund_no(Util.getStringFromMap(map,"out_refund_no_" + i,""));
            refundQuery.setRefund_id(Util.getStringFromMap(map,"refund_id_" + i,""));
            refundQuery.setRefund_channel(Util.getStringFromMap(map,"refund_channel_" + i,""));
            refundQuery.setRefund_fee(Util.getIntFromMap(map,"refund_fee_" + i));
            refundQuery.setSettlement_refund_fee(Util.getIntFromMap(map,"settlement_refund_fee_" + i));
            refundQuery.setCoupon_type(Util.getStringFromMap(map,"coupon_type_" + i, ""));
            refundQuery.setCoupon_refund_fee(Util.getIntFromMap(map,"coupon_refund_fee_" + i));
            refundQuery.setCoupon_refund_count(Util.getIntFromMap(map,"coupon_refund_fee_" + i));
            refundQuery.setRefund_status(Util.getStringFromMap(map,"refund_status_" + i,""));
            refundQuery.setRefund_recv_accout(Util.getStringFromMap(map,"refund_recv_accout_" + i,""));
            List<CouponRefund> couponRefundList = new ArrayList<>();
            if(refundQuery.getCoupon_refund_count() > 0) {
                for(int j=0; j < refundQuery.getCoupon_refund_count(); j++) {
                    CouponRefund couponRefund = new CouponRefund();
                    couponRefund.setCoupon_refund_batch_id(Util.getStringFromMap(map,"coupon_refund_batch_id_" + i + "_"+j,""));
                    couponRefund.setCoupon_refund_id(Util.getStringFromMap(map,"coupon_refund_id_" + i + "_"+j,""));
                    couponRefund.setCoupon_refund_fee(Util.getIntFromMap(map,"coupon_refund_fee_" + i + "_"+j));
                    couponRefundList.add(couponRefund);
                }
            }
            refundQuery.setCouponRefunds(couponRefundList);
            refundQueryList.add(refundQuery);
        }
        return refundQueryList;
    }

    /**
     * 解析xml数据到map中
     * @param xmlString
     * @return
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {

        //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream is =  Util.getStringStream(xmlString);
        Document document = builder.parse(is);

        //获取到document里面的全部结点
        NodeList allNodes = document.getFirstChild().getChildNodes();
        Node node;
        Map<String, Object> map = new HashMap<String, Object>();
        int i=0;
        while (i < allNodes.getLength()) {
            node = allNodes.item(i);
            if(node instanceof Element){
                map.put(node.getNodeName(),node.getTextContent());
            }
            i++;
        }
        return map;
    }

    /**
     * map转换成xml字符串
     * @param map
     * @return
     */
    public static String getXmlFromMap(Map<String,Object> map) {
        xstream.alias("xml", map.getClass());
        return xstream.toXML(map);
    }

    /**
     * 将对象转换成xml字符串
     * @param xmlObj
     * @return
     */
    public static String getXmlFromObject(Object xmlObj) {
        xstream.alias("xml", xmlObj.getClass());
        return xstream.toXML(xmlObj);
    }

    /**
     * 扩展xstream，使其支持CDATA块
     */
    private static XStream xstream = new XStream(new XppDriver() {
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                // 对所有xml节点的转换都增加CDATA标记
                boolean cdata = true;

                @SuppressWarnings("unchecked")
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }

                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });
}
